home *** CD-ROM | disk | FTP | other *** search
Korn shell script | 1997-08-26 | 8.3 KB | 290 lines |
- #!/bin/ksh
- # @(#) chuser.ksh 1.2.1 97/07/30
- # 94/01/22 john h. dubois iii (john@armory.com)
- # 94/09/09 Added check for non-matchable password.
- # 94/10/11 Use % instead of - as sed separator, to guarantee that it cannot
- # appear in a user name.
- # 94/10/13 Added -u option. Added sanity checks.
- # 94/10/22 Do not allow _ in login names.
- # 94/11/14 Fixed regexp used for checking if login name is in use.
- # Fixed unsetting of old uid.
- # 95/10/28 Added t option.
- # 96/01/20 Create tempfile more carefully.
- # 97/07/30 Added f option.
-
- # todo: when changing uid, check for processes, ipc objects, etc. owned by
- # the old uid and warn about them.
-
- # @(#) mktempfile 1.0 96/05/22
- # 96/01/20 jhdiii
-
- # Usage: mkfiles name ...
- # Creates the named files with some attempt at security.
- # This will be more reliable if user do not have chown authority.
- # Any file that contains no / characters is created in the user's tempdir.
- # If TMP was not set, it is set by this function (but not exported).
- # Returns 0 on success; prints a diagnostic message & returns 1 on failure.
- function mkfiles {
- typeset file files
- typeset -i i=0
-
- : ${TMP:=$TMPDIR}
- : ${TMP:=/tmp}
- for file; do
- [[ "$file" != */* ]] && file="$TMP/$file"
- files[i]=$file
- let i+=1
- done
- set -- "${files[@]}"
-
- rm -f "$@" || {
- # hopefully rm will have printed a more specific message.
- print -u2 "Could not remove pre-existing files."
- return 1
- }
- for file; do
- # Use >> to avoid 0'ing the file in case a symlink was just made from
- # the filename to something we don't want to truncate
- >> "$file" || {
- print -u2 "Could not create temp file $file"
- return 1
- }
- # These are very suspicious
- [ -s "$file" ] && {
- print -u2 "New tempfile is not empty!!!"
- return 1
- }
- [ -O "$file" ] || {
- print -u2 "Do not own $file!!!"
- return 1
- }
- done
- # Could do some more stuff here, but anyone concerned with security should
- # have chown authorization off for most users.
- return 0
- }
-
- # Usage: mktempfile name
- # Creates a tempfile name tempdir/#name$$, and sets the global mktempfile_ret
- # to that name. tempdir is a temp directory in $TMP, $TMPDIR, or /tmp, and $$
- # is the PID of the current process.
- # name should be 8 characters or less, so that the resulting filename will
- # not be more than 14 characters long (a limit on some machines).
- # Returns 0 on success, prints a diagnostic message & returns 1 on failure.
- function mktempfile {
- typeset file="#$1$$"
- mkfiles "$file"
- mktempfile_ret="$TMP/#$1$$"
- }
-
- # Usage: Error [-f] message ...
- # If -f is given, this is considered a fatal error even if tell is true
- function Error {
- typeset warn
-
- if [ $# -gt 1 -a "$1" = -f ]; then
- warn=false
- shift
- else
- warn=$tell
- fi
- if $warn; then
- print -u2 "$name: Warning: $* (continuing)."
- else
- print -u2 "$name: Fatal error: $*. Aborting."
- exit 1
- fi
- }
-
- ### Start of main program
-
- name=${0##*/}
- unset newuid
- tell=false
- chkname=true
-
- Usage="Usage: One of the following forms:
- $name -h
- $name [-ft] oldname newname
- $name [-ft] -u<uid> oldname [newname]"
-
- while getopts :hftu: opt; do
- case $opt in
- h)
- print \
- "$name: change a user's account name or uid.
- $Usage
- Either -u<uid> or [newname] or both may be given. If -u<uid> is given, the
- user's old uid is changed to <uid>. If newname is given, the user's login name
- is changed from oldname to newname and the user's home directory is renamed
- ($name assumes that the last component of the login directory is the same as
- the user name). In both cases, the user's protected password database
- information is updated. $name will fail if the named user is logged in, or if
- newname or the new uid is already in use.
- Other options:
- -f: Force change even if newname is not a legal user name (for example, if
- newname starts with a digit). Be very careful when using this option.
- -h: Print this help.
- -t: Tell what would be done, without doing it. This changes some fatal errors
- to warnings."
- exit 0
- ;;
- f)
- chkname=false
- ;;
- t)
- tell=true
- ;;
- u)
- newuid=$OPTARG
- if [[ "$newuid" != +([0-9]) ||
- "$newuid" -lt 1 || "$newuid" -gt 32767 ]]; then
- Error -f "Bad value given for new uid"
- fi
- grep "^[^:]*:[^:]*:$newuid:" /etc/passwd > /dev/null &&
- Error "new uid '$newuid' is already in use"
- ;;
- +?)
- Error -f "options should not be preceded by a '+'"
- ;;
- :)
- Error -f "Option '$OPTARG' requires a value. Use -h for help."
- ;;
- ?)
- Error -f "$OPTARG: bad option. Use -h for help"
- ;;
- esac
- done
-
- # remove args that were options
- let OPTIND=OPTIND-1
- shift $OPTIND
-
- # Must have 1 or 2 args; if only 1 arg, must have given -u option.
- if [ $# -ne 1 -a $# -ne 2 -o $# -eq 1 -a -z "$newuid" ]; then
- Error -f "Not enough arguments. Use -h for help.\n"
- fi
-
- oldname=$1
- if [ $# -eq 1 ]; then
- newname=$1 # replace it with itself
- else
- newname=$2
- egrep "^$newname:" /etc/passwd > /dev/null &&
- Error "$newname is already a user"
- # getty man page says _ should not be used in a login name,
- # and every other non-alphanum except - is used as a metachar
- # somewhere where a login name would be used.
- if $chkname &&
- [[ "$newname" != [a-z]+([-a-z0-9]) || "$newname" = ?????????* ]]; then
- Error "$newname: invalid user name"
- fi
- fi
-
- # Get olduid even if -u not given, since it is used as a check for whether
- # a good username was given.
- olduid=$(awk -F: "/^$oldname:/ { print \$3;}" /etc/passwd)
-
- [ -z "$olduid" ] && Error -f "$oldname is not a user"
-
- # If not changing uid, unset olduid, lest old uid be replaced with nothing
- [ -z "$newuid" ] && unset olduid
-
- # At this point:
- # oldname is the name of the user whose account is being changed.
- # newname is either the new name for the account, or is the same as oldname
- # if the name is not being changed.
- # If the uid is being changed, olduid is the old uid and newuid is the new uid.
- # If the uid isn't being changed, they are both null.
-
- if who | egrep "^$oldname "; then
- Error "$oldname is logged in; cannot change account record"
- fi
-
- # ap output looks like this:
- # stealth:x:1032:50:Jonathan D. Campbell:/u/stealth:/bin/ksh
- # stealth:u_name=stealth:u_id#1032:\
- # :u_pwd=x:\
- # :u_type=general:u_lock@:chkent:
- # ENDOFGROUPS::0:
-
- # Save user account configuration, translated to new name
- # Replace user name at start of line followed by a colon,
- # and user name after an = or / followed by colon (u_name and home dir fields)
- mktempfile chuser. || {
- print -u2 "$name: Exiting."
- exit 1
- }
- tmpfile=$mktempfile_ret
-
- print "Saving account profile for $newname in $tmpfile"
- # Line 1 subs passwd and tcb login name.
- # Line 2 subs tcb u_name.
- # Line 3 subs passwd directory name.
- # Line 4 subs passwd uid.
- # Line 5 subs passwd uid.
- ap -gd "$oldname" | sed "
- s/^$oldname:/$newname:/
- s/=$oldname:/=$newname:/
- s%/$oldname:%/$newname:%
- s/:$olduid:/:$newuid:/
- s/#$olduid:/#$newuid:/
- " > $tmpfile
-
- grep :u_pwd=x: $tmpfile && grep "^$newname:x:" $tmpfile &&
- print -u2 "$name: Warning: account profile contains non-matchable password."
-
- uhome=`awk -F: "\"$oldname"'" == $1 { print $6; }' /etc/passwd`
-
- if $tell; then
- print "Would remove user $oldname."
- else
- print "Removing user $oldname..."
- rmuser "$oldname"
- status=$?
- if [ $status -ne 0 ]; then
- # Do NOT remove tmpfile; rmuser may exit nonzero even if user is
- # mostly removed!
- #rm $tmpfile
- Error -f \
- "Removal of user $oldname failed (rmuser exited with status $status)"
- fi
- fi
-
- if $tell; then
- print "Would restore user $newname with account profile:"
- cat $tmpfile
- else
- print "Restoring user $newname with account profile:"
- cat $tmpfile
- ap -r -f $tmpfile
- fi
-
- if [ $# -eq 2 ]; then
- # Replace /oldname$ with /newname
- newhome=`print -r -- $uhome|sed "s/\\/$oldname\\$/\\/$newname/"`
-
- if $tell; then
- print -r "Would move home dir $uhome to $newhome"
- else
- print -r "Moving home dir $uhome to $newhome"
- fi
- if [ -d $uhome ]; then
- $tell || mvdir $uhome $newhome
- else
- print -u2 \
- "$uhome is not a directory! If it is a symlink, move it by hand."
- fi
- fi
-
- [ -n "$newuid" ] && print \
- "Remember to change the ownership of any files
- from $olduid to $newuid ($newname)."
-
- [ "$oldname" != "$newname" ] && print \
- "Remember to create a mail alias, if appropriate, to forward mail
- from $oldname to $newname. Here's an alias line to cut & paste:
- $oldname $newname"
-
- rm $tmpfile
-